package com.beiding.oa2;

import com.beiding.test.A;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.*;

/*
    分析对象
 */


/*
    对象切换类型的关键操作:
        1. 放入新类型的目标对象
        2. 由属性分析对象向持有者对象执行赋值操作
        3. 将第二步中未涉及到的其他字段进行属性拷贝
        4. 将object切换为target

 */
public class AnalyzeObject {

    //持有信息
    private Set<HeldData> heldDataSet = Collections.EMPTY_SET;

    private Object object;

    AnalyzeObject(Object object) {//这里的对象为什么会为空?????
        setObject(object);
    }

    private Object target;

    private Map<String, Field> objectFieldMap = Collections.EMPTY_MAP;

    private Map<String, Field> targetFieldMap = Collections.EMPTY_MAP;

    //数组对象的叶子元素类型
    private Class leafType;

    private Class targetLeafType;

    public boolean isArray() {//TODO 对象为什么为空
        return object.getClass().isArray();
    }

    public boolean componentIsObject() {
        return Object.class.isAssignableFrom(object.getClass().getComponentType());
    }

    public boolean isClass() {
        return object instanceof Class;
    }

    public void newType(Class type) {

        if (about(type)) {//类型相同不做处理
            return;
        }

        if (isClass()) {
            target = type;
        } else if (isArray()) {
            targetLeafType = type;

            //TODO 创建同纬度的数组
            target = createArray(object, targetLeafType);

        } else {
            handledOnBuildField = new HashSet<>();//只有类型发生迁移才有该属性
            if (targetFieldMap == Collections.EMPTY_MAP) {
                targetFieldMap = new HashMap<>();
            }

            Class t = type;

            while (t != Object.class) {

                Field[] declaredFields = t.getDeclaredFields();
                for (Field declaredField : declaredFields) {
                    declaredField.setAccessible(true);
                    targetFieldMap.put(declaredField.getDeclaringClass().getName() + " " + declaredField.getName(), declaredField);
                }
                t = t.getSuperclass();
            }

            target = AnalyzeUtils.allocateInstance(type);

        }

    }


    public boolean about(Class type) {

        if (object == null) {
            return false;
        }

        if (isClass()) {
            return type == object;
        } else if (isArray()) {
            return leafType == type;
        } else {
            return type == object.getClass();
        }
    }

    //执行对象切换
    public void shift() {
        if (handledOnBuildField != null) {
            targetFieldMap.forEach((k, v) -> {
                if (!handledOnBuildField.contains(k)) {
                    Field field = objectFieldMap.get(k);
                    if (field == null) {
                        System.out.println("原对象不存在属性:" + k);
                    } else {
                        try {
                            v.set(target, field.get(object));
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }

                    }
                }
            });
        }
        handledOnBuildField = null;
        object = target;
        objectFieldMap = targetFieldMap;
        target = null;
        leafType = targetLeafType;
        targetLeafType = null;
        targetFieldMap = null;
    }

    private Set<String> handledOnBuildField;

    //执行构建操作
    public void build() {

        Object that;
        if (target == null) {
            that = object;
        } else {
            that = target;
        }
        for (HeldData heldData : heldDataSet) {//TODO 空白

            AnalyzeObject holder = heldData.getHolder();
            if (heldData.getHeldType() == HeldType.FILED) {
                String field = heldData.getField();
                if (holder.target != null) {
                    Field f = holder.targetFieldMap.get(field);
                    if (f == null) {
                        System.out.println("目标持有者(" + holder.target.getClass() + ")已丢失属性:" + field);
                    } else {
                        try {
                            f.set(holder.target, that);
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    holder.handledOnBuildField.add(field);
                } else {
                    Field f = holder.objectFieldMap.get(field);
                    try {
                        f.set(holder.object, that);
                    } catch (IllegalAccessException e) {
                        throw new RuntimeException(e);
                    }
                }
            } else {
                if (holder.target != null) {
                    ((Object[]) holder.target)[heldData.getIndex()] = that;
                } else {
                    ((Object[]) holder.object)[heldData.getIndex()] = that;
                }
            }
        }

    }

    public Map<String, Field> getObjectFieldMap() {
        return objectFieldMap;
    }

    private void setObject(Object object) {

        Class type = object.getClass();

        if (type != Class.class) {
            if (type.isArray()) {
                type = type.getComponentType();
                while (type.isArray()) {
                    type = type.getComponentType();
                }
                leafType = type;
            } else {

                if (objectFieldMap == Collections.EMPTY_MAP) {
                    objectFieldMap = new HashMap<>();
                }
                while (type != Object.class) {

                    Field[] declaredFields = type.getDeclaredFields();
                    for (Field declaredField : declaredFields) {
                        declaredField.setAccessible(true);
                        objectFieldMap.put(declaredField.getDeclaringClass().getName() + " " + declaredField.getName(), declaredField);
                    }
                    type = type.getSuperclass();
                }

            }
        }
        this.object = object;
    }

    public Object getObject() {
        return object;
    }

    public Set<HeldData> getHeldDataSet() {
        return heldDataSet;
    }

    public void addHeld(AnalyzeObject holder, Field field) {
        if (heldDataSet == Collections.EMPTY_SET) {
            heldDataSet = new HashSet<>();
        }
        HeldData heldData = new HeldData();
        heldData.setHeldType(HeldType.FILED);
        heldData.setHolder(holder);
        heldData.setField(field.getDeclaringClass().getName() + " " + field.getName());
        heldDataSet.add(heldData);
    }

    public void addHeld(AnalyzeObject holder, int index) {
        if (heldDataSet == Collections.EMPTY_SET) {
            heldDataSet = new HashSet<>();
        }
        HeldData heldData = new HeldData();
        heldData.setHeldType(HeldType.COMPONENT);
        heldData.setHolder(holder);
        heldData.setIndex(index);
        heldDataSet.add(heldData);
    }


    private static Object createArray(Object arr, Class leafComponentType) {
        char[] chars = arr.getClass().getName().toCharArray();
        int d = 0;
        for (char aChar : chars) {
            if (aChar == 'L') {
                break;
            }
            d++;
        }
        Class t = leafComponentType;
        for (int i = 1; i < d; i++) {
            t = Array.newInstance(t, 0).getClass();
        }
        return Array.newInstance(t, Array.getLength(arr));
    }

}
